import os
import sys
import json
import pyglet
import getopt

from time import time 
from engine.animation import parse_animations, Character
from engine.material import Material, MaterialImage
from engine.fixed_resolution import FixedResolutionViewport
from engine.characters import Link, draw_characters
from engine import config
from engine import camera

from pyglet.window import Window, key
from pyglet.graphics import Batch
from pyglet.sprite import Sprite

from engine.editor.shared import *
from engine.pyglet_extensions import *

from engine.tile_engine import TileEngine

from pprint import pprint


target_width, target_height = 512, 448

PATH_ANIM = "game/animations"
PATH_CHAR = "game/characters"
PATH_MTR = "game/materials"


class Game(Window):

    def __init__(self, map_name=None):
        super(Game, self).__init__(width=target_width, height=target_height)

        # Keystate handlers
        self.keys = key.KeyStateHandler()
        self.push_handlers(self.keys)

        # Viewport
        self.viewport = FixedResolutionViewport(self, 
                target_width, target_height, filtered=False)
        self.character_viewport = FixedResolutionViewport(self, 
                target_width, target_height, filtered=False)


        # Tiles
        self.tiles = []
        
        # Create the rendering batches
        self.batches = [Batch() for x in range(10)]
    
        # Camera
        self.camera = camera.FlatCamera(0, 0, self.width, self.height)

        # Tiling engine
        self.tile_engine = TileEngine(camera=self.viewport)

        # Animation, character, and material setups
        self._anim = config.animations
        self._char = config.character_configs
        self._mtr = config.materials

        # Initialize
        self.setup()
    
        # Player instance
        self.player = Link(player=True, keys=self.keys)
        self.player.character.x = target_width / 2
        self.player.character.y = target_height / 2

        # Load map
        if map_name:
            self.load_map(map_name)

    def cycle_resolutions(self):
        if self.viewport.width == 640:
            self.viewport.resize(256, 224, self.player)
            self.character_viewport.resize(256, 224, self.player)
            self.width, self.height = 512, 448
        elif self.viewport.width == 256:
            self.viewport.resize(512, 448, self.player)
            self.character_viewport.resize(512, 448, self.player)
            self.width, self.height = 512, 448
        elif self.viewport.width == 512:
            self.viewport.resize(400, 300, self.player)
            self.character_viewport.resize(400, 300, self.player)
            self.width, self.height = 800, 600 
        elif self.viewport.width == 400:
            self.viewport.resize(640, 480, self.player)
            self.character_viewport.resize(640, 480, self.player)
            self.width, self.height = 640, 480 

    def load_animations(self):
        """Parses and loads the available animations for the game.
        Reads all the files (*.anim) and creates the necessary
        animations."""
        start = time()
        files = os.listdir(PATH_ANIM)
        total = 0
        for f in files:
            name, ext = f.split(".")
            if ext != "anim":
                continue
            self._anim[name], count = parse_animations(f)
            total += count
        elapsed = (time() - start)
        print "Parsed %s files and created %s animations in %s seconds\n" % (
            len(files), total, elapsed)


    def load_characters(self):
        """Parses and loads the available character descriptors for the game.
        Reads all the files (*.char) and creates the necessary character
        descriptors for entities in the game."""
        print "========= Loading characters... =========="
        start = time()
        files = os.listdir(PATH_CHAR)
        for f in files:
            name, ext = f.split(".")
            if ext != "char":
                continue
            fd = open("%s/%s" % (PATH_CHAR, f))
            self._char[name] = json.load(fd)
            size = len(self._char[name].keys())
            print "%s: %s attributes" % (name, size)
        elapsed = (time() - start)
        print "------------------------------------------"
        print "Parsed and created %s characters in %s seconds\n" % (
            len(files), elapsed)

    def reload_tiles(self):
        # Load all the necessary materials
        files = os.listdir(MATERIAL_PATH)
        self.tile_batch = []
        for f in files:
            name, ext = f.split(".")
            if ext != 'mtr':
                continue
            mtrs = json.load(open("%s/%s" % (MATERIAL_PATH, f)))
            for item in mtrs:
                if mtrs[item].get('entity'):
                    cfg = mtrs[item]
                    entity = str(cfg['entity'])
                    im = __import__('engine.entities', fromlist=[entity])
                    im = im.__dict__[entity]
                    #self._entities[item] = im
                    mat = Material(mtrs[item], item)
                    self._mtr[item] = mat

                else:
                    mat = Material(mtrs[item], item)
                    self.tile_batch.append(mat)
                    self._mtr[item] = mat


    def load_world(self):
        # here be testing
        import random

    def setup(self):
        """Initializing the game. All setup routines are called here."""
        print "=============== Loading... ==============="
        print "=  Legend of Zelda: A Link to the Past   ="
        print "==========================================\n"
        start = time()
        self.load_animations()
        self.load_characters()
        self.reload_tiles()
        self.load_world()
        elapsed = (time() - start)
        print "------------------------------------------"
        print "Initializing finished in %s seconds.\n" % elapsed

    def on_draw(self):
        '''Draw the scene, assuming the fixed resolution viewport and projection
        have been set up.  This just draws the rotated polygon.'''
        batch = self.tile_engine.generate_batch()
        self.viewport.x = -self.player.x
        self.viewport.y = -self.player.y
        self.clear()
        self.camera.project()
        self.viewport.begin()
        batch.draw()
        self.viewport.end()
        self.character_viewport.begin()
        draw_characters()
        self.character_viewport.end()

    def load_map(self, map_name):
        try:
            if os.path.exists(map_name + '-optimized-tile.png'):
                print 'Found optimized version. Loading that.'
                img = pyglet.image.load(map_name + '-optimized-tile.png')
                self._mtr['Optimized'] = MaterialImage(img)
                tile = {
                        'm': 'Optimized',
                        'x': 0, 'y': 0, 
                        'w': img.width, 'h': img.height, 
                        'p': 0, 'z': 0
                }
                self.insert_tile(tile)
                tiles = [tile]
            else:
                tiles = json.load(open("%s" % map_name))
                map(self.insert_tile, tiles)
            self.tile_engine.generate_batch()
            self.on_draw()
            self.tile_engine.generate_batch()
            print 'Loading %s tiles.' % len(tiles)
        except IOError:
            print '%s was not found.' % map_name

    def insert_tile(self, tile):
        """Inserts a given tile at a (x, y, height)"""
        mat_name = tile['m']
        x, y, z, p = tile['x'], tile['y'], tile['z'], tile['p']
        w, h = tile['w'], tile['h']
        mat = self._mtr[mat_name]
        batch = self.batches[z]
        img = mat.image
        tile_group = pyglet.graphics.OrderedGroup(p)
        tile = TileableSprite(img, x, y, tile_width=w, tile_height=h,
            group=tile_group)

        self.tile_engine.add_tile(tile)
        #self.tiles.append(tile)

    def on_key_press(self, k, modifiers):
        if k == key.PLUS:
            self.cycle_resolutions()
        if k == key.L:
            self.tile_engine.toggle()


def main():
    # --map=foo.map
    # -m foo.map
    map_name = None
    opts, args = getopt.getopt(sys.argv[1:], "m:", ["map="])
    for o, a in opts:
        if o in ("-m", "--map"):
            map_name = a

    game = Game(map_name)
    pyglet.app.run()

if __name__ == "__main__":
    main()
